var template = `
<div class="task">
    <h3>任务管理</h3>
    <button @click="doAdd" v-if="!isEdit">新增</button>
    <button @click="doCancel" v-if="isEdit">取消</button>
    <button @click="doRefresh">刷新</button>
    <button @click="doTest">测试</button>
    
    <Form :data="editModel" :showModel="formShowModel" :schema="schema" v-if="isEdit && !isTest" @cancel="doCancel" @submit="doSubmit"></Form>
    <Form :data="editModel" :showModel="formTestModel" :schema="schema" v-if="isEdit && isTest" @cancel="doCancel" @submit="doSubmitTest"></Form>
    <List :list="list" :showModel="listShowModel" :schema="schema">
        <template v-slot="{item}">
            <button @click="doStart(item)" v-if="!item.running">开始</button>
            <button @click="doPause(item)" v-if="item.running&&!item.paused">暂停</button>
            <button @click="doResume(item)" v-if="item.running&&item.paused">恢复</button>
            <button @click="doStop(item)" v-if="item.running">停止</button>
            <button @click="doEdit(item)" :disabled="item.running">修改</button>
            <button @click="doDelete(item)" :disabled="item.running">删除</button>
        </template>
    </List>
    <div v-if="banMsg && banMsg.length > 0">
        <div v-for="key in banMsg">
            {{banMsg[key]}}
        </div>
    </div>
    <Crack></Crack>
    <div v-if="isHealthCheck" class="health">
        <div class="message" :class="'health-'+healthState">
            <span>
            健康监控{{healthState?'正常':'异常'}}
            {{timeUnit(pastSecond)}}
            阀值 {{timeoutStr}}
            </span>
            <button @click="()=>healthCheckPaused=!healthCheckPaused">
            {{healthCheckPaused?'继续':'暂停'}}
            </button>
            <div v-for="key,index in healthMsg">[{{index+1}}] {{key}}</div>
        </div>
    </div>
    <Runner v-if="sharedState.runTask" @update="doUpdate" @banMsg="doBanMsg"></Runner>
</div>
`;


let Task = {
    name: 'Task',
    data() {
        return {
            list: [],
            isEdit: false,
            isTest: false,
            sharedState: store.state,
            // swagger中的schema
            schema: '任务信息',
            model: {
                taskName: '任务-' + new Date().fmt('yyyy年MM月dd日-HH-mm-ss'),
                threadSize: 1,
                first: '',
                total: '',
                current: '',
                percentSuccess: '',
                timeoutExecTime: '',
                running: false,
                paused: false,
                firstExecTime: '',
                lastExecTime: '',
            },
            modelTest: {
                url: 'https://www.instagram.com/graphql/query/?query_id=17888483320059182&id=4143607182&first=40',
                proxy: 'http://127.0.0.1:1080',
                headers: {
                    'Cookie': 'ds_user_id=51132914782; csrftoken=dNCZEujTqwDLbBPdi6hxExVBy2ymggfE; sessionid=51132914782%3AwJ2vpixHZOofYl%3A8',
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36',
                },
            },
            listShowModel: {
                taskName: '',
                // threadSize: '',
                // first: '',
                total: '',
                current: '',
                // percentSuccess: '',
                running: false,
                paused: false,
                updatedTime: {
                    type: 'date',
                    formatter: 'yyyy-MM-dd HH:mm:ss'
                },
            },
            formShowModel: {
                taskName: '',
                threadSize: 1,
                // first: '',
                // total: '',
                // current: '',
                // percentSuccess: '',
                // running: false,
                // updatedTime: '',
            },
            formTestModel: {
                url: '',
                proxy: '',
                headers: {
                    type: 'map',
                },
            },
            editModel: {},
            heartBeat: false,
            banMsg: '',
            isUnMounted: false,
            isHealthCheck: appConfig.isHealthCheck,
            healthCheckPaused: false,
            pastSecond: 0,
            healthState: true,
            healthMsg: [],
            timeoutStr: -1,
        }
    },
    components: {List, Form, Runner, Crack},
    template,
    mounted: () => {
        const {proxy} = Vue.getCurrentInstance();
        console.log('%s mounted', proxy.$options.name);
        proxy.doGetAll();
        if (proxy.isHealthCheck) {
            proxy.doHealthCheck();
        }
    },
    unmounted() {
        this.isUnMounted = true;
    },
    methods: {
        timeUnit(second) {
            return timeUnit(second);
        },
        async doHealthCheck() {
            let startId = localStorage.getItem("startId")
            this.timeoutStr = this.timeUnit(Math.floor(appConfig.healthCheckIntervalMs / 1000))
            let autoStart = localStorage.getItem("autoStart")
            console.log('healthCheck start startId', startId)
            let isExecAutoStart = false
            while (!this.isUnMounted) {
                if (autoStart && !isExecAutoStart) {
                    let queryHash = this.sharedState.queryHash
                    if (queryHash) {
                        isExecAutoStart = true
                        await this.isHealthCheckPaused()
                        setTimeout(this.doAutoStart, 5000)
                    } else {
                        let msg = ['自动启动任务无法执行', 'queryHash未破解']
                        console.log(...msg);
                        this.$message({message: msg, level: 'error'});
                    }
                }
                await this.isHealthCheckPaused()
                let {lastMessageTime = 0} = this.sharedState;
                let now = Date.now();
                let past = now - lastMessageTime;
                if (past > appConfig.healthCheckIntervalMs) {
                    this.healthState = false;
                    this.sharedState.healthState = this.healthState;
                    this.pastSecond = Math.floor(past / 1000);
                    let msg = ['健康检查超期 将在5秒后尝试重启任务', this.timeUnit(this.pastSecond)];
                    this.healthMsg.push(msg[0])
                    console.log(...msg);
                    this.$message({message: msg, level: 'error'});
                    localStorage.setItem("autoStart", "true")
                    setTimeout(() => {
                        console.log('刷新页面')
                        let nextTimeout = 0;
                        // 停止任务
                        let {runTask} = this.sharedState;
                        if (runTask) {
                            nextTimeout += 1000;
                            this.doStop(runTask)
                        }
                        // 发送邮件
                        nextTimeout += 5000;
                        let emailCount = localStorage.getItem("emailCount") || 0;
                        if (!(emailCount && emailCount >= appConfig.emailCountIgnore)) {
                            emailCount++
                            localStorage.setItem("emailCount", emailCount);
                        } else {
                            this.doSendMail().then(() => {
                                let msg = `${this.timeUnit(nextTimeout / 1000)} 后刷新页面`
                                this.healthMsg.push(msg)
                                console.log(msg)
                                console.log('邮件发送成功, 清除邮箱计数');
                                localStorage.setItem("emailCount", "0");
                                // 刷新页面
                                setTimeout(() => history.go(0), nextTimeout)
                            })
                        }
                    }, 10000)
                    await this.sleep(20000);
                } else {
                    this.healthState = true;
                    this.sharedState.healthState = this.healthState;
                }
                this.pastSecond = Math.floor(past / 1000);
                await this.sleep(1000);
            }
            console.log('healthCheck stopped')
        },
        async sleep(sleepMs = this.sleepTimeMs) {
            return new Promise(((resolve, reject) => {
                setTimeout(() => {
                    resolve()
                }, sleepMs)
            }))
        },
        async isHealthCheckPaused() {
            while (this.healthCheckPaused) {
                await this.sleep(300)
            }
        },
        async doSendMail() {
            let emailList = [];
            await this.$api.notifyEmail.getAll().then(res => {
                if (res.data && res.data.code === 0) {
                    emailList = res.data.data
                    emailList = emailList
                        .filter(mail => mail.enabled && mail.email)
                        .map(mail => mail.email)
                    let msg = `获取邮件列表成功! 待通知邮箱(${emailList.length}个) ${emailList}`
                    this.healthMsg.push(msg)
                    console.log(msg, res)
                } else {
                    let msg = `获取邮件列表失败!`
                    this.healthMsg.push(msg)
                    console.log(msg, res, emailList)
                }
            }).catch(err => {
                let msg = `获取邮件列表异常!`
                this.healthMsg.push(msg)
                console.log('notifyEmail err', err)
            })
            let mailDto = {
                content: `${new Date().fmt('[爬虫告警][yyyy-MM-dd HH:mm:ss] ')}爬虫任务健康检查失败, 将在5秒后尝试重启任务; 请及时进行检查!\n[检查周期] ${this.timeoutStr}`,
                title: `《${new Date().fmt('[爬虫告警][yyyy-MM-dd HH:mm:ss] ')}任务健康检查告警》`,
                to: ""
            };
            this.healthMsg.push(`即将发送告警邮件`)
            this.healthMsg.push(`[标题] ${mailDto.title}`)
            this.healthMsg.push(`[内容] ${mailDto.content}`)
            for (let i = 0; i < emailList.length; i++) {
                const email = emailList[i];
                let msg = `尝试给${email}发送邮件`
                this.healthMsg.push(msg)
                let emailObj = {
                    content: mailDto.content,
                    title: mailDto.title,
                    to: email,
                }
                await this.$api.eMail.send(emailObj).then(res => {
                    if (res.data && res.data.code === 0) {
                        let msg = `给${email}发送邮件成功!`
                        this.healthMsg.push(msg)
                        console.log(msg, res)
                    } else {
                        let {data = {msg: ''}} = res
                        let msg = `给${email}发送邮件失败!${data.msg}`
                        this.healthMsg.push(msg)
                        console.log(msg, res)
                    }
                }).catch(err => {
                    let msg = `给${email}发送邮件异常!`
                    this.healthMsg.push(msg)
                    console.log('发送邮件异常!', err)
                })
            }
        },
        doAutoStart() {
            let msg = '触发自动启动任务'
            this.healthMsg.push(msg)
            console.log(msg)
            let startId = localStorage.getItem("startId")
            let find = this.list.filter(e => e.id === startId)
            let item = null;
            if (find && find.length > 0) {
                item = find[0];
            } else {
                if (this.list && this.list.length > 0) {
                    item = this.list[0]
                }
            }
            if (item) {
                let {taskName} = item
                let msg = ['健康检查 启动任务', taskName]
                this.healthMsg.push(...msg)
                console.log('健康检查 启动任务', item)
                this.doStart(item)
            } else {
                let msg = ['健康检查', '无可用启动任务']
                this.healthMsg.push(...msg)
                console.log(...msg, this.list)
                this.$message({message: msg, level: 'error'})
            }
        },
        doRefresh() {
            this.$api.task.getAll().then(res => {
                if (res.data && res.data.code === 0) {
                    this.list = res.data.data;
                    let msg = '刷新成功';
                    this.$message({message: msg});
                } else {
                    let msg = '接口调用失败';
                    console.error(msg, res);
                    this.$message({message: msg, level: 'error'});
                }
            }).catch(e => {
                let msg = '口调用接失败';
                console.error(msg, e);
                this.$message({message: msg, level: 'error'});
            })
        },
        doGetAll() {
            this.$api.task.getAll().then(res => {
                if (res.data && res.data.code === 0) {
                    this.list = res.data.data;
                    let {runTask, healthState = true} = this.sharedState;
                    if (!runTask) {
                        let list = deepClone(this.list)
                        list.sort(item => item.running ? -1 : 1).forEach(item => {
                            if (!healthState) {
                                healthState = true;
                                this.doStart(item);
                            }
                        })
                    }
                } else {
                    let msg = '接口调用失败';
                    console.error(msg, res);
                    this.$message({message: msg, level: 'error'});
                }
            }).catch(e => {
                let msg = '接口调用失败';
                console.error(msg, e);
                this.$message({message: msg, level: 'error'});
            })
        },
        doAdd() {
            this.editModel = deepClone(this.model);
            this.isEdit = true;
        },
        doEdit(item) {
            this.editModel = deepClone(item);
            this.isEdit = true;
        },
        doDelete(item) {
            this.$api.task.delete(item).then(res => {
                if (res.data && res.data.code === 0) {
                    let msg = '删除成功!';
                    console.log(msg, res.data);
                    this.$message({message: msg});
                    this.isEdit = false;
                    this.doGetAll()
                } else {
                    let msg = '删除失败!';
                    console.error(msg, res.data);
                    this.$message({message: msg, level: 'error'});
                }
            }).catch(e => {
                let msg = '删除失败!';
                console.error(msg, e);
                this.$message({message: msg, level: 'error'});
            })
        },
        doSubmit() {
            this.doUpdate(this.editModel);
        },
        doUpdate(item) {
            if (!item) {
                let msg = '保存失败!';
                console.error(msg, item);
                this.$message({message: msg, level: 'error'});
                return;
            }
            this.$api.task.update(item).then(res => {
                if (res.data && res.data.code === 0) {
                    let msg = '保存成功!';
                    console.log(msg, res.data);
                    this.$message({message: msg});
                    this.isEdit = false;
                    this.doGetAll()
                } else {
                    let msg = '保存失败!';
                    console.error(msg, res.data);
                    this.$message({message: msg, level: 'error'});
                }
            }).catch(e => {
                let msg = '保存失败!';
                console.error(msg, e);
                this.$message({message: msg, level: 'error'});
            })
        },
        doBanMsg(msg = null) {
            this.banMsg = msg;
        },
        doCancel() {
            this.isEdit = false;
            this.isTest = false;
        },
        doStart(item) {
            let {runTask} = this.sharedState;
            if (runTask) {
                let msg = "已有任务在运行 " + runTask.taskName;
                console.warn('[%s] '.replace('%s', new Date().fmt()) + msg);
                this.$message({message: ['已有任务在运行', runTask.taskName], level: 'error'});
            } else {
                this.sharedState.runTask = item;
                let {id} = item;
                localStorage.setItem("startId", id)
                item.running = true;
                let msg = [`任务启动`, item.taskName];
                console.log(...msg);
                this.$message({message: msg});
            }
        },
        doStop(item) {
            item.running = false;
            item.paused = false;
            let {runTask} = this.sharedState;
            if (runTask && runTask.running && runTask.id === item.id) {
                runTask.running = false;
                runTask.paused = false;
                this.sharedState.runTask = null;
            } else {
                // 非运行任务强行停止
                this.doUpdate(item);
            }
        },
        doPause(item) {
            let {runTask} = this.sharedState;
            if (runTask && !runTask.paused && runTask.id === item.id) {
                item.paused = true;
                runTask.paused = true;
            }
        },
        doResume(item) {
            let {runTask} = this.sharedState;
            if (runTask && runTask.paused && runTask.id === item.id) {
                item.paused = false;
                runTask.paused = false;
            }
        },
        doTest(item) {
            this.editModel = deepClone(this.modelTest);
            this.isEdit = true;
            this.isTest = true;
        },
        doSubmitTest() {
            this.$api.task.test(this.editModel).then(res => {
                if (res.data && res.data.code === 0) {
                    let msg = '测试成功!';
                    console.log(msg, res.data);
                    this.$message({message: msg + '\n' + res.data.data, duration: 10});
                    // this.isEdit = false;
                    // this.doGetAll()
                } else {
                    let msg = '测试失败!';
                    console.error(msg, res.data);
                    this.$message({message: msg, level: 'error'});
                }
            }).catch(e => {
                let msg = '测试失败!';
                console.error(msg, e);
                this.$message({message: msg, level: 'error'});
            })
        },
    },
    watch: {
        list(newVal, oldVal) {
            console.log('list changed')
        },
        sharedState(newVal, oldVal) {
            console.log('sharedState changed')
        },
    },
};